﻿#include <doctest/doctest.h>
#include "log.h"

#include "fmt/core.h"
#include "fmt/ranges.h"

#include "yaml-cpp/yaml.h"
#include "utility.hpp"
#include <algorithm>
#include <cmath>
#include <exception>
#include <nlohmann/json.hpp>

//#define BOOST_STACKTRACE_USE_ADDR2LINE
#include <boost/stacktrace.hpp>
#include <coroutine>
#include <csignal>
#include <ranges>
#include <regex>

#include <stdexcept>
#include <string>
#include <thread>
/*#include <range/v3/all.hpp>*/
/*
import small_utils;
import fast_mutex;
import range_utils;
import map_util;
import message_bus;
*/
/*import fast_utils:small_utils;
import fast_utils:fast_mutex;
import fast_utils:range_utils;
import fast_utils:map_util;
import fast_utils:message_bus;*/
import fast_utils;
using namespace fast::util;

TEST_CASE(" json_test") {
    // 为了使用方便，可以让json命名空间内所有标识符在此文件中可见
    using json = nlohmann::json;

    //方式1：在序列化后字符串后后添加 _json后缀
    json j = "{ \"happy\": true, \"pi\": 3.141 }"_json;

    //方式2：从原生字符串
    auto j2 = R"(
      {
        "happy": true,
        "pi": 3.141
      }
    )"_json;

    //方式3：显示的调用 json::parse
    auto j3 = json::parse(R"({"happy": true, "pi": 3.141})");

    //方式4：从json文件中读取
    std::ifstream ifs("test.json");
    json jf = json::parse(ifs);

    // 显示的转换
    std::string s = j.dump();    // {"happy":true,"pi":3.141}

    // 写入文件
    json jsonfile;
    jsonfile["happy"] = true;
    jsonfile["pi"] = 3.141;
    std::ofstream file("test.json");
    file << jsonfile;

    // special iterator member functions for objects
    for (json::iterator it = jsonfile.begin(); it != jsonfile.end(); ++it) {
        std::cout << it.key() << " : " << it.value() << "\n";
    }

    // the same code as range for
    for (auto &el: jsonfile.items()) {
        std::cout << el.key() << " : " << el.value() << "\n";
    }

    // even easier with structured bindings (C++17)
    for (auto &[key, value]: jsonfile.items()) {
        std::cout << key << " : " << value << "\n";
    }

    // find an entry
    if (jsonfile.contains("foo")) {
        // there is an entry with key "foo"
    }

    //获取迭代器 or via find and an iterator
    if (jsonfile.find("foo") != jsonfile.end()) {
        // there is an entry with key "foo"
    }

    // 删除 delete an entry
    jsonfile.erase("foo");

    /*  std::vector<int> c_vector {1, 2, 3, 4};
      json j_vec(c_vector);
  // [1, 2, 3, 4]

      std::deque<double> c_deque {1.2, 2.3, 3.4, 5.6};
      json j_deque(c_deque);
  // [1.2, 2.3, 3.4, 5.6]

      std::list<bool> c_list {true, true, false, true};
      json j_list(c_list);
  // [true, true, false, true]

      std::forward_list<int64_t> c_flist {12345678909876, 23456789098765, 34567890987654, 45678909876543};
      json j_flist(c_flist);
  // [12345678909876, 23456789098765, 34567890987654, 45678909876543]

      std::array<unsigned long, 4> c_array {{1, 2, 3, 4}};
      json j_array(c_array);
  // [1, 2, 3, 4]

      std::set<std::string> c_set {"one", "two", "three", "four", "one"};
      json j_set(c_set); // only one entry for "one" is used
  // ["four", "one", "three", "two"]

      std::unordered_set<std::string> c_uset {"one", "two", "three", "four", "one"};
      json j_uset(c_uset); // only one entry for "one" is used
  // maybe ["two", "three", "four", "one"]

      std::multiset<std::string> c_mset {"one", "two", "one", "four"};
      json j_mset(c_mset); // both entries for "one" are used
  // maybe ["one", "two", "one", "four"]

      std::unordered_multiset<std::string> c_umset {"one", "two", "one", "four"};
      json j_umset(c_umset); // both entries for "one" are used
  // maybe ["one", "two", "one", "four"]

     std::map<std::string, int> c_map { {"one", 1}, {"two", 2}, {"three", 3} };
      json j_map(c_map);
      // {"one": 1, "three": 3, "two": 2 }

      std::unordered_map<const char*, double> c_umap { {"one", 1.2}, {"two", 2.3}, {"three", 3.4} };
      json j_umap(c_umap);
      // {"one": 1.2, "two": 2.3, "three": 3.4}

      std::multimap<std::string, bool> c_mmap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
      json j_mmap(c_mmap); // only one entry for key "three" is used
      // maybe {"one": true, "two": true, "three": true}

      std::unordered_multimap<std::string, bool> c_ummap { {"one", true}, {"two", true}, {"three", false}, {"three", true} };
      json j_ummap(c_ummap); // only one entry for key "three" is used
      // maybe {"one": true, "two": true, "three": true}

     */

}

// java 锁
TEST_CASE(" synchronized") {
    struct P {
        int a, b;
    };
    std::vector<P> v = {{1, 1},
                        {1, 2},
                        {2, 1}};

    std::vector<int> arr{1, 2, 3};

    std::mutex mutex;

    for (const auto &item: arr) {
        /*synchronized(mutex) {
            LOG_INFO << item;
        }*/
    }
}

// 函数指针调试
std::string test_function_point(std::string one, std::string two) {
    LOG_WARNING << "one = " << one << " two = " << two;
    return "abc";
}


// 函数指针
TEST_CASE(" function_point") {

// 取地址的函数指针
    std::string (*function_quote)(std::string, std::string);

// 取值的函数指针
    std::string (*function_value)(std::string, std::string);

    function_quote = &test_function_point;

    function_value = test_function_point;
    (*function_value)(" hi ! one ", " hi ! two");
}

class singleton {
public:
    singleton() {

    }

    std::string singletonValue;

    static singleton &instance() {
        static std::shared_ptr<singleton> shared_ptr = std::make_shared<singleton>();
        static singleton *singleton = shared_ptr.get();
        return *singleton;
    }

    static singleton &instance2() {
        static std::shared_ptr<singleton> shared_ptr = std::make_shared<singleton>();
        static singleton *singleton = shared_ptr.get();
        return *singleton;
    }

    static singleton &instance3(int i) {
        LOG_INFO << "3 test hava arg function         automatically init???";
        static std::shared_ptr<singleton> shared_ptr = std::make_shared<singleton>();
        static singleton *singleton = shared_ptr.get();
        return *singleton;
    }

    static singleton &instance4() {
        LOG_INFO << "4 test hava arg function         automatically init???";
        static std::shared_ptr<singleton> shared_ptr = std::make_shared<singleton>();
        static singleton *singleton = shared_ptr.get();
        return *singleton;
    }

};

TEST_CASE(" singleton") {
    singleton::instance().singletonValue = "test abc";

    auto &singleton_one = singleton::instance();
    LOG_INFO << singleton_one.singletonValue;

    singleton_one.singletonValue = "test b";

    auto &singleton_two = singleton::instance();
    LOG_WARNING << singleton_two.singletonValue;

    singleton_one.instance2().singletonValue = "q1";

    singleton_two.instance2().singletonValue = "q2";

    LOG_WARNING << "singleton_one.instance2().singletonValue = " << singleton_one.instance2().singletonValue;

    LOG_WARNING << "singleton_two.instance2().singletonValue = " << singleton_two.instance2().singletonValue;

    LOG_WARNING << "singleton_two.instance().singletonValue = " << singleton_two.instance().singletonValue;


    // decltype 必须原始类型,不能用auto修饰
    singleton test_decltype;
    decltype(test_decltype) decltype_singleton;
    LOG_WARNING << decltype_singleton.instance().singletonValue;
}

TEST_CASE(" map_util") {


    std::map<std::string, std::string> map{
            {"this",  "100"},
            {"can",   "100"},
            {"be",    "100"},
            {"const", "100"},
    };

    std::for_each(map.begin(), map.end(), [](auto item) {

    });

    auto *ptr = MapUtil::GetPtr(map, "this");

    *ptr = "200";

    LOG_INFO << map["this"];

    auto default_value = MapUtil::GetDefault(map, "undefined");

    //dbg(default_value.empty());
    LOG_INFO << default_value.empty();

    LOG_INFO << typeid(default_value).name();

    LOG_INFO << default_value.size();

    std::string mapped_type_value = "abcd";
    auto mapped_type_default_value = MapUtil::GetRefDefault(map, "undefined", mapped_type_value);
    bool is_true = mapped_type_value.data() == mapped_type_default_value.data();

    LOG_INFO << "address:" << is_true;

    LOG_INFO << typeid(mapped_type_default_value).name();

    LOG_INFO << mapped_type_default_value;
    if (mapped_type_default_value == mapped_type_value) {
        LOG_INFO << &mapped_type_default_value << "value is equal,but memory address not equal" << &mapped_type_value;
    }

}
// 指针指向二维数组 堆
TEST_CASE(" point_some_heap") {

    //.... 创建一个 4 行 5列的数组
    int rowsNum = 4;
    int colsNum = 5;

    float **a = new float *[rowsNum];
    for (int i = 0; i < rowsNum; i++) {
        a[i] = new float[colsNum];
    }

    //... 释放二级指针
    for (int i = 0; i < rowsNum; i++) {
        delete[] a[i];
    }
    delete[]a;

    auto target = new int[100];

    // 二级指针创建方式
    int **intList = new int *[100];

    LOG_INFO << typeid(intList).name();

    for (int i = 0; i < 100; ++i) {
        target[i] = i;
        intList[i] = &target[i];
    }

    for (int i = 0; i < 100; ++i) {
        // 向后偏移5个地址
        LOG_INFO << *(intList[i] + 5);
    }

#if c
    //... c语言版本  4 行 5列的数组
    //竖的格子申请好了
    int **p = (int **) malloc(4 * sizeof(int *));

    //右面格子为int
    for (int i = 0; i < 4; i++) {
      p[i] = (int *) malloc(5 * sizeof(int));
    }

    for (int i = 0; i < 4; i++) {
      for (int j = 0; i < 5; j++) {
        printf("%p\n", &p[i][j]);
      }
    }

    //... 版本2
    int(*p1)[4] = (int(*)[4])malloc(3 * 4 * sizeof(int));
    for (int i = 0; i < 3; i++)
    {
      for (int j = 0; i < 4; j++)
      {
        printf("%p\n", &p1[i][j]);
      }
    }
    free (p);
#endif
}

Singleton <std::string> test;

SingletonPtr <std::string> test3;

TEST_CASE(" static_one") {
    fmt::print("test {} //", 1);
    auto t =
            std::tuple<int, float, std::string, char>(42, 1.5f, "this is tuple", 'i');
    fmt::format("123456 {}", t);

    auto string = test.GetInstance();
    LOG_INFO << string;
    string->append("11111");
    LOG_INFO << string->c_str();

    decltype(test) test2;
    auto string_one = test2.GetInstance();
    string->append("2222");
    LOG_INFO << test2.GetInstance()->c_str();

    LOG_INFO << string_one;
    auto string_ptr = test3.GetInstance();

    string_ptr->append("hello world ");

    LOG_INFO << string_ptr->c_str();
}

// This is definitely not async-signal-safe. Let's hope it doesn't crash or hang.
void handler(int signo) {
    if (std::ofstream file_stream("my_stacktrace.log", std::ios::trunc); file_stream.is_open()) {
        std::stringstream ss;
        ss << boost::stacktrace::stacktrace();
        std::cout << boost::stacktrace::stacktrace() << std::endl;
        file_stream.write(ss.str().c_str(), ss.str().size());
        file_stream.close();
    }

    // Raise signal again. Should usually terminate the program.
    //raise(signo);
}

TEST_CASE(" string") {
    using namespace std::literals;
    if ("gif"sv == "png"sv) {

    }
    if ("gif"s == "png"s) {

    }

    auto a = std::string{"hello，你好世界！"};
    LOG_ERROR << a;

    int i = 1, j = 2;
    std::swap(i, j);

    std::printf(" i = %d , j = %d", i, j);

    std::string s("c++11 test split");

    std::regex reg(" "); // "\\s+" for blank
    std::vector<std::string> v(std::sregex_token_iterator(s.begin(), s.end(), reg, -1), std::sregex_token_iterator());

    for (const auto &str: v)
        std::cout << "==>" << str << "<<==" << std::endl;
}


TEST_CASE(" yaml") {
    using namespace std;
    YAML::Node config = YAML::LoadFile("config.yaml");

    try {
        cout << "Node type " << config.Type() << endl;
        cout << "skills type " << config["a"].Type() << endl;

        cout << "name:" << config["b"].as<string>() << endl;
        cout << "sex:" << config["c"].as<string>() << endl;
        cout << "age:" << config["d"].as<int>() << endl;
        cout << "skills c++:" << config["1"]["1"]["1"].as<int>() << endl;
        cout << "skills java:" << config["skills"]["java"].as<int>() << endl;
        cout << "skills android:" << config["skills"]["android"].as<int>() << endl;
        cout << "skills python:" << config["skills"]["python"].as<int>() << endl;

        for (YAML::const_iterator it = config["skills"].begin(); it != config["skills"].end(); ++it) {
            cout << it->first.as<string>() << ":" << it->second.as<int>() << endl;
        }

        YAML::Node test1 = YAML::Load("[1,2,3,4]");
        cout << " Type: " << test1.Type() << endl;

        YAML::Node test2 = YAML::Load("1");
        cout << " Type: " << test2.Type() << endl;

        YAML::Node test4 = YAML::Load("{'id':1,'degree':'senior'}");
        cout << " Type: " << test4.Type() << endl;

        ofstream fout("testconfig.xml");

        config["score"] = 99;

        fout << config;

        fout.close();

    }
    catch (std::exception &ptr) {
        std::cout << ptr.what() << std::endl;
        LOG_STACK;
        //LOG_ERROR << boost::stacktrace::stacktrace();
    } catch (...) {
        std::exception_ptr ex = std::current_exception();
        try {
            std::rethrow_exception(ex);
        }
        catch (std::bad_exception const &) {
            // This will be printed.
            std::cout << "Bad exception" << std::endl;
        }
    }


}


/// 消息总线
TEST_CASE(" TestMessageBus") {
    using namespace std;
    struct A {
        void Test(int x) { cout << x << endl; }

        void GTest() {
            cout << "it is a test" << endl;
        }

        void HTest(int x) const {
            cout << "it is a HTest" << endl;
        }
    };


    A a;
    MessageBus<> bus;
/*
    bus.Attach<int>("aa", &A::Test, &a);
    int x = 3;
    bus.SendReq("aa", 3);

    bus.Attach<int>("hh", &A::HTest, &a);
    bus.SendReq("hh", x);
    bus.Attach("bb", &A::GTest, &a);
    bus.SendReq("bb");


    bus.Attach<int>("gg", [](int i) {
        {
            cout << "it is a gg " << i << endl;
        }
    });
    bus.SendReq("gg", 3);

    bus.Attach("gg", [] {
        cout << "it is a GG" << endl;
    });
    bus.SendReq("gg");
*/
};

class Person {
public:
    std::string name;
    int age;
    std::string city;

    NLOHMANN_DEFINE_TYPE_INTRUSIVE(Person, name, age, city)

    friend std::ostream &operator<<(std::ostream &os, const Person &person) {
        os << "name: " << person.name << " age: " << person.age << " city: " << person.city;
        return os;
    }
};


TEST_CASE(" group_by") {

    std::vector<Person> vt = {{"aa", 20, "shanghai"},
                              {"bb", 25, "beijing"},
                              {"cc", 25, "nanjing"},
                              {"dd", 20, "nanjing"}};

    std::pair pairs{"ee", 20};

    RangeUtils <std::vector<Person>> range(vt);

    /// <summary>
    ///  指定一个函数对象
    /// </summary>
    auto r1 = range.group_by<int>([](const Person &person) {
        return person.age; ///< 年龄为 key
    });

    cat_item(r1);


    auto r2 = range.group_by<std::string>([](const Person &person) {
        return person.name; ///< 名字为 key
    });

    cat_item(r2);


    auto r3 = range.group_by<std::string>([](const Person &person) {
        return person.city; ///< 城市为 key
    });

    cat_item(r3);

    auto r4 = range.group_by<std::tuple<std::string, int>>([](const Person &person) {
        return std::tie(person.name, person.age); ///< 将 tuple（名字和年龄组成） 作为 key
    });


    /// <summary>
    ///  指定两个函数对象
    /// </summary>
    auto r5 = range.group_by<std::tuple<std::string, int>,
            std::tuple<std::string>>([](const Person &person) {
                                         return std::tie(person.name, person.age); ///< 将 tuple（名字和年龄组成） 作为 key
                                     },
                                     [](const Person &person) {
                                         return std::tie(person.city); ///< 将 tuple（城市） 作为 value
                                     });

}

// 两数之和
/*输入：nums = [2,7,11,15], target = 9
输出：[0,1]
解释：因为 nums[0] + nums[1] == 9 ，返回 [0, 1] 。
*/
class Solution {
public:
    // 双for
    std::vector<int> twoSum_for(std::vector<int> &nums, int target) {

        for (int i = 0; i < nums.size(); ++i) {
            for (int j = 0; j < nums.size(); ++j) {
                if (((nums[i] + nums[j]) == target) && i != j) {
                    return {i, j};
                }
            }
        }

        return {};
    }

    // hash table
    std::vector<int> twoSum_hash(std::vector<int> &nums, int target) {

        std::unordered_map<int, int> hashtable;
        for (int i = 0; i < nums.size(); ++i) {
            auto it = hashtable.find(target - nums[i]);
            if (it != hashtable.end()) {
                return {it->second, i};
            }
            hashtable[nums[i]] = i;
        }

        return {};
    }
};

TEST_CASE(" twoSum") {
    //std::printl("[{:n}]", std::tuple{1, 2, "Hi"s});

}

TEST_CASE(" Yang_Hui_Triangle") {
    int a[10][10], i, j;

    for (i = 0; i < 10; i++) {
        for (j = 0; j <= i; j++)//如果j<n的话，杨辉三角的上半部分就会是乱码
        {
            if (i == j || j == 0) {
                a[i][j] = 1;
            } else {
                a[i][j] = a[i - 1][j - 1] + a[i - 1][j];
            }
        }
    }

    for (i = 0; i < 10; i++) {
        for (j = 0; j <= i; j++) {
            printf("%5d", a[i][j]);
        }
        printf("\n");
    }
    printf("asdsabva\b\'123213\'sdqweqwe\n");
}